/*
	Copyright (C) 2010 Salil Bhagurkar

	This file is part of k3

	k3 is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	k3 is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with k3.  If not, see <http://www.gnu.org/licenses/>.
*/

/*SImple irq management code*/

#include <shared.h>

#define NR_IRQS 16

struct irq_handler {
	int (*irq_handler)(void *data);
	void *data;
}irq_handlers[NR_IRQS];


static volatile int pend_schedule = 0, in_irq = 0;
static struct fs *sched_fs;

/**
This function is called by the interrupt handler in assembly passing irq number as
the parameter. This function will in turn call the handler which is configured
over that irq. Earlier kernels supported multiple handlers over a single irq. But
we will do that here only when needed ;)
 */
static void main_irq(int irq_no)
{
	in_irq = 1;
	if(irq_handlers[irq_no].irq_handler)
		irq_handlers[irq_no].irq_handler(irq_handlers[irq_no].data);
	irq_ended(irq_no);

	if(pend_schedule) {
		if(sched_fs) {
			pend_schedule = 0;
			in_irq = 0;
			fs_write(NULL, sched_fs, NULL, 0, 0); //Reschedule
		}
	}
	in_irq = 0;
}

void arch_configure_interrupt_handler(int irq_no, int (*irq_handler)(void *data), void *data)
{
	irq_handlers[irq_no].irq_handler = irq_handler;
	irq_handlers[irq_no].data = data;
	enable_irq(irq_no);
}

void arch_remove_interrupt_handler(int irq_no)
{
	irq_handlers[irq_no].irq_handler = NULL;
	irq_handlers[irq_no].data = NULL;
	disable_irq(irq_no);
}

void arch_schedule_schedule_on_irq()
{
	if(!in_irq) {
		if(sched_fs) {
			fs_write(NULL, sched_fs, NULL, 0, 0); //Reschedule
		}
	} else
		pend_schedule = 1;
}

int init_sched_arch()
{
	sched_fs = fsparse_get("/sched");
	return 0;
}

int init_irq()
{
	memset(irq_handlers, 0, sizeof(struct irq_handler) * NR_IRQS);
	//Here the assembly function arch_set_irq_call configures the asm irq handler
	//to call the main_irq upon irq
	arch_set_irq_call(main_irq);
	//This will configure default irqs for all 256 interrupts
	_set_all_irqs();
	//This will remap the irqs on 8259 and then configure all as enabled
	init_pic();
	//Finally enable all interrupts
	asm volatile ("sti");
	return 0;
}
